Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add QUIC Address Discovery to iroh #3049

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open

Conversation

ramfox
Copy link
Contributor

@ramfox ramfox commented Dec 14, 2024

Description

There were two main things to "solve" on the iroh side:

  1. gross circular dependency between magicsock and the quinn endpoint
  2. needing to handle QAD packets in a special way, like we do with STUN packets that get sent from net-report

The first was accomplished by figuring out how to make MagicSock::spawn be the location that builds the quinn::Endpoint. This is what was changed:

  • implemented AsyncUdpSocket on MagicSock itself, rather than magicsock::Handle
  • magicsock::Handle now contains the quinn::Endpoint
  • we now pass down a server_config as a MagicSock::Option
  • in MagicSock::spawn:
    • after building the MagicSock we now build the quinn::Endpoint using Arc<MagicSock> as the AsyncUdpSocket
    • then we clone the quinn::Endpoint and passed it to the magicsock::Actor (which is independent of MagicSock)
    • give the quinn::Endpoint to the magicsock::Handle, which is the actual struct that we use to interact with the magicsocket
  • the iroh::Endpoint now interacts with the quinn::Endpoint using msock.endpoint() in all places

The second was accomplished by keeping a list of special "QAD addresses" on the NodeMap (NodeMap::qad_addrs), and using specific methods to add_qad_addrs, get_qad_addrs_for_send and get_qad_addrs_for_recv to deal with the fickle way that the quinn::Endpoint expects SocketAddrs to behave when it dials and when it receives packets:

  • before we do a net-report, we first attempt to resolve the RelayUrls in the RelayMap to get the SocketAddrs that we expect we will dial when we do a QAD probe
  • ~we add those addresses to the NodeMap ~
  • on the "send" side of the AsyncUdpSocket, after we do our normal checks for QuicMappedAddrs, we then check to see if the QuicMappedAddr is actually just a normal socket addr that we expect to use for QAD. If so, we just send the packets using the get_qad_addr_for_send address
  • on the "recv" side of the AsyncUdpSocket, after we check to see if the recv'd address can be mapped to a QuicMappedAddr & therefore can be received using our normal process, we then check to see if the address is one that we expect for QAD. If so, we make sure to associate the packet with the get_qad_addr_for_recv address and pass it along

The most "unreliable" bits of this are due to dns. Before running a net report, we now have to do dns discovery for all the RelayUrls. I've capped this at 300 ms but it means that until we cache the dns responses then we have this delay before starting net-report. I've also done a bit of a cheat: when we initially start the magicsock::Actor, I've added a call to resolve_qad_addrs that has a timeout of 10 ms, just to send out the dns packets and hopefully get a jump on caching the responses.

The second was accomplished by creating a new IpMappedAddrs struct that keeps track of IpMappedAddrs to the actual SocketAddr that it represents. This is akin to how we deal with QuicMappedAddr (which is now called NodeIdMappedAddr. netreport now takes an optional IpMappedAddrs, and when it resolves a RelayUrl, it adds the resolved IP addr to the IpMappedAddrs, and uses the mapped address when sending on QUIC. In iroh, we check to see if the destination or source address is an IpMappedAddr or a NodeIdMappedAddr. If so, it maps the addresses correctly before returning the transmit to the endpoint or sending the transmit over UDP.

Most interesting bit

So...if someone adds an IP address to IpMappedAddrs, we can allow folks to use the Endpoint as a sort of normal quinn::Endpoint, bypassing our special iroh holepunching sauce.

depends on #3032

Change checklist

  • Self-review.
  • Documentation updates following the style guide, if relevant.
  • Tests if relevant.
  • All breaking changes documented.

@ramfox ramfox changed the base branch from main to ramfox/net-report-simplify December 14, 2024 07:27
Copy link

github-actions bot commented Dec 14, 2024

Documentation for this PR has been generated and is available at: https://n0-computer.github.io/iroh/pr/3049/docs/iroh/

Last updated: 2025-01-10T21:28:42Z

@ramfox ramfox force-pushed the ramfox/qad-in-iroh branch 3 times, most recently from 2a51de3 to d18f3d1 Compare December 14, 2024 07:47
@ramfox ramfox self-assigned this Dec 14, 2024
@flub
Copy link
Contributor

flub commented Dec 16, 2024

The second was accomplished by keeping a list of special "QAD addresses" on the NodeMap

The downside of this approach is that you need to lock the NodeMap once again on every single try_send and poll_recv call. That's not great.

What if we made a separate mapping outside of the NodeMap for this? We can make a new type IpMappedAddr (and rename QuicMappedAddr to NodeIdMappedAddr while we're at it). It would be a mirror of the current QuicMappedAddr but with a different ADDR_SUBNET.

So we use a new subnet in our existing IPv6 Unique Local Addr space. This subnet works just like the existing subnet otherwise. But is used to map real IP addresses to other fake ones.

This allows the code that needs to check whether something is a IpMappedAddr or a QuicMappedAddr (or NodeIdMappedAddr) to simply check the IPv6 prefix with no need to get any locks until it is know which one you have.

Copy link
Contributor

@flub flub left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The way of connecting the quinn::Endpoint into the MagicSock is nice!

iroh/src/endpoint.rs Outdated Show resolved Hide resolved
iroh/src/magicsock.rs Show resolved Hide resolved
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I already said on a top-level comment, I think I'd prefer qad_addrs to be a separate map on the magicsock itself, with it's own lock independent of the NodeMap since this doesn't have anything to do with the nodemap. The addresses can then be distinguished based on the IPv6 subnet prefix used.

iroh/src/magicsock.rs Outdated Show resolved Hide resolved
iroh/src/magicsock.rs Outdated Show resolved Hide resolved
@ramfox ramfox force-pushed the ramfox/net-report-simplify branch from 1e24436 to 4f9db39 Compare December 16, 2024 16:48
@ramfox ramfox force-pushed the ramfox/qad-in-iroh branch 2 times, most recently from 59b2744 to ee67d96 Compare January 7, 2025 22:41
@ramfox ramfox changed the base branch from ramfox/net-report-simplify to main January 8, 2025 16:40
@ramfox ramfox force-pushed the ramfox/qad-in-iroh branch from ee67d96 to 201ef1e Compare January 10, 2025 00:13
“ramfox” added 7 commits January 10, 2025 15:17
`MagicSock::spawn` creates a `quinn::Endpoint`. `MagicSock` is now an `AsyncUdpSocket`, and can be passed into the `quinn::Endpoint`. `magicsock::Handle` now owns the `quinn::Endpoint`, and `iroh::Endpoint` interacts with the `quinn::Endpoint` through `Handle::endpoint()`.

This allows us to pass the `quinn::Endpoint` to the `magicsock::Actor` for use in QAD, without any circular dependencies.
@ramfox ramfox force-pushed the ramfox/qad-in-iroh branch from 9b57aab to 8b69c62 Compare January 10, 2025 20:21
@ramfox ramfox force-pushed the ramfox/qad-in-iroh branch from 8b69c62 to 96aa2a5 Compare January 10, 2025 21:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: 🏗 In progress
Development

Successfully merging this pull request may close these issues.

2 participants